/*File FocusWindow02.java
Copyright 2004 R.G.Baldwin
Rev 07/30/04
This program illustrates the ability of a Window
to gain and lose the focus, and to fire events
when the focus is gained or lost.  This
capability is new to SDK V1.4.

The program also illustrates the ability of a
JFrame to fire events when the state of the
JFrame changes.  This capability is also new to
SDK V1.4.  A list of the possible states is
provided later in this discussion.

The program places two JFrame objects on the
screen, one above the other.  The JFrame object
on the top contains two JButton objects, both
of which are focusable.

The JFrame object on the bottom contains a single
Canvas object, which is not focusable.  Therefore
the JFrame object on the bottom contains no
focusable components.

The JFrame object on the bottom illustrates the
use of focus on a Window that contains no
focusable components.  The coordinates of an
invisible point are displayed on the Canvas near
the location of the invisible point.  When this
object has the focus, pressing the arrow keys
will change the coordinates of the invisible
point, thus modifying and moving the displayed
coordinate values.  This is accomplished by
servicing KeyEvents on the object, thus
demonstrating that the JFrame object actually
has the focus.

The JFrame object on the top contains two
JButton objects labeled respectively:

Regain focus
Send focus to B

Clicking the first of these two buttons will
cause the JFrame object on the top to gain the
focus, will cause the JFrame object on the bottom
to lose the focus, and will also cause the
JButton object to gain the focus.

Clicking the button labeled "Send focus to B"
will momentarily cause the JFrame object and the
button to gain the focus, but will immediately
transfer the focus to the JFrame object on the
bottom.

The JFrame objet on the bottom will also gain the
focus if it is clicked with the mouse.

As each JFrame object gains and loses focus,
information about the gain and loss of focus is
displayed on the screen, using both high-level
event handling and low-level event handling.

The high-level approach makes use of an object
of type FocusListener.

The low-level approach makes use of the following
methods:

enableEvents(AWTEvent.WINDOW_EVENT_MASK)
processWindowEvent(WindowEvent e)
processWindowFocusEvent(WindowEvent e)
processWindowStateEvent(WindowEvent e)

The program also uses low-level event handling to
display information about other WindowEvent types
such as the following.  Note that this list
includes focus and state events, which are new
to SDK V1.4.

WINDOW_ACTIVATED
WINDOW_CLOSED
WINDOW_CLOSING
WINDOW_DEACTIVATED
WINDOW_DEICONIFIED
WINDOW_ICONIFIED
WINDOW_OPENED
WINDOW_STATE_CHANGED
WINDOW_GAINED_FOCUS
WINDOW_LOST_FOCUS

Note that there is no WindowEvent type that is
fired when a JFrame is maximized.  However, a
windowStateChanged event is fired when a JFrame
is maximized.  Information about the state of
the window is encapsulated in the WindowEvent
object and can be extracted using the following
methods:

getOldState
getNewState

The fact that the JFrame has been maximized can
be extracted from the state of the window.  The
following states are defined:

NORMAL
ICONIFIED
MAXIMIZED_HORIZ
MAXIMIZED_VERT
MAXIMIZED_BOTH

This state information is defined as values of
type int, corresponding to the descriptive
constants in the above list.  These constants are
defined in the Frame class.

The actual value associated with each constant in
the above list (and many other constants as well)
can be determined by looking the constant up in
the API documentation and selecting the hyperlink
named Constant Field Values.

Although this program uses Swing components and
draws the coordinate values on a Canvas object,
it is a trivial matter to modify the program
to use only AWT components, and to eliminate the
Canvas object.  In that case, the coordinate
values are drawn directly on the Frame.  Since
the Frame doesn't contain any components at all,
it is absolutely guaranteed that it doesn't
contain any focusable components.

It is also easy to convert the program to one
that uses JDialog objects in place of JFrame
objects.  Of course, JDialog objects can't be
minimized or maximized, so that portion of the
program having to do with the Window State has
no meaning with respect to JDialog objects.

Tested using JDK 1.4 under WinXP
 ************************************************/

package au.com.lastweekend.jim.experimental;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class WindowFocusTester extends JFrame {

    public static void main(String[] args) {

        // Instantiate an object of this class, which
        // will, in turn, instantiate an object of
        // the class named GraphicsGUI.
        WindowFocusTester winA = new WindowFocusTester();
    }// end main
    // ---------------------------------------------//

    public WindowFocusTester() {// constructor

        // Instantiate a GraphicsGUI object, The
        // GraphicsGUI object will be identified as B
        // in the screen display.
        final GraphicsGUI winB = new GraphicsGUI();

        // Now construct the object of this class,
        // prepare it to handle events, and make it
        // visible. This object will be identified
        // as A in the screen display.
        // Begin by setting some properties.
        setTitle("A Copyright 2004, R.G.Baldwin");
        getContentPane().setLayout(new FlowLayout());
        setSize(400, 100);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // Instantiate two buttons, prepare them to
        // handle FocusEvents, and add them to the
        // JFrame.
        JButton regainButton = new JButton("Regain focus");
        regainButton.addFocusListener(new FocusLstnr());
        getContentPane().add(regainButton);

        JButton sendButton = new JButton("Send focus to B");
        sendButton.addFocusListener(new FocusLstnr());
        getContentPane().add(sendButton);

        // Prepare one of the buttons to handle
        // ActionEvents using an anonymous inner
        // class.
        sendButton.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {

                winB.requestFocus();
            }// end actionPerformed
        }// end new ActionListener
                );// end addActionListener

        // Prepare the JFrame to handle events using
        // the low-level process methods.
        enableEvents(AWTEvent.WINDOW_EVENT_MASK);

        // Prepare the JFrame to handle events using
        // the high-level listener methods.
        addWindowFocusListener(new WindowLstnr());
        addWindowStateListener(new WindowLstnr());

        // Make this JFrame visible. This will steal
        // the focus from the other JFrame.
        setVisible(true);

    }// end constructor

    // -------------------------------------------//

    // The next three methods are low-level event
    // handler methods that are new to V1.4. These
    // methods are identical to methods having the
    // same names defined for the class named
    // GraphicsGUI, except that the screen output
    // identifies the object as A rather than B.

    // Sample screen output for this method:
    // Low:processWindowEvent A
    // 205 WINDOW_ACTIVATED
    protected void processWindowEvent(WindowEvent e) {

        System.out.println();
        System.out.println("Low:processWindowEvent A");
        System.out.print(e.getID() + " ");
        System.out.println(getEventType(e.getID()));
        super.processWindowEvent(e);
    }// end processWindowEvent

    // -------------------------------------------//

    // Sample screen output for this method:
    // Low:processWindowFocusEvent A
    // 207 WINDOW_GAINED_FOCUS
    protected void processWindowFocusEvent(WindowEvent e) {

        System.out.println();
        System.out.println("Low:processWindowFocusEvent A");
        System.out.print(e.getID() + " ");
        System.out.println(getEventType(e.getID()));
        super.processWindowFocusEvent(e);
    }// end processWindowFocusEvent

    // -------------------------------------------//

    // Sample screen output for this method:
    // Low:processWindowStateEvent A
    // 209 WINDOW_STATE_CHANGED
    protected void processWindowStateEvent(WindowEvent e) {

        System.out.println();
        System.out.println("Low:processWindowStateEvent A");
        System.out.print(e.getID() + " ");
        System.out.println(getEventType(e.getID()));
        super.processWindowStateEvent(e);
    }// end processWindowStateEvent

    // -------------------------------------------//

    // Method to convert event ID values to text
    // descriptions.
    String getEventType(int ID) {

        if (ID == WindowEvent.WINDOW_ACTIVATED) {
            return "WINDOW_ACTIVATED";
        } else if (ID == WindowEvent.WINDOW_CLOSED) {
            return "WINDOW_CLOSED";
        } else if (ID == WindowEvent.WINDOW_CLOSING) {
            return "WINDOW_CLOSING";
        } else if (ID == WindowEvent.WINDOW_DEACTIVATED) {
            return "WINDOW_DEACTIVATED";
        } else if (ID == WindowEvent.WINDOW_DEICONIFIED) {
            return "WINDOW_DEICONIFIED";
        } else if (ID == WindowEvent.WINDOW_ICONIFIED) {
            return "WINDOW_ICONIFIED";
        } else if (ID == WindowEvent.WINDOW_OPENED) {
            return "WINDOW_OPENED";
        } else if (ID == WindowEvent.WINDOW_STATE_CHANGED) {
            return "WINDOW_STATE_CHANGED";
        } else if (ID == WindowEvent.WINDOW_GAINED_FOCUS) {
            return "WINDOW_GAINED_FOCUS";
        } else if (ID == WindowEvent.WINDOW_LOST_FOCUS) {
            return "WINDOW_LOST_FOCUS";
        } else {
            return "Unknown event type";
        }// end else
    }// end getEventType

}// end class FocusWindow02
// =============================================//

// Objects of this class are registered on the two
// JButton objects to report high-level
// focusGained and focusLost events.
// Sample outputs from these methods:
// High:focusGained on Regain focus button
// High:focusLost on Regain focus button
// High:focusGained on Send focus to B button
// High:focusLost on Send focus to B button
class FocusLstnr implements FocusListener {

    public void focusGained(FocusEvent e) {

        System.out.println("High:focusGained on " + ((JButton) e.getSource()).getText() + " button");
    }// wns focusGained

    // -------------------------------------------//

    public void focusLost(FocusEvent e) {

        System.out.println("High:focusLost on " + ((JButton) e.getSource()).getText() + " button");
    }// end focusLost
}// end class FocusLstnr

// =============================================//

// Objects of this class are registered on the two
// JFrame objects to report high-level focus and
// state events on the objects.

// Sample outputs from the methods are:
// High:windowGainedFocus A
// High:windowLostFocus A
// High:windowGainedFocus B
// High:windowLostFocus B
// High:windowStateChanged NORMAL to ICONIFIED B
// High:windowStateChanged NORMAL to ICONIFIED A
// High:windowStateChanged ICONIFIED to NORMAL A
// High:windowStateChanged ICONIFIED to NORMAL B

class WindowLstnr implements WindowFocusListener, WindowStateListener {

    public void windowGainedFocus(WindowEvent e) {

        System.out.print("High:windowGainedFocus ");
        System.out.println(((JFrame) e.getSource()).getTitle().substring(0, 1));
    }// end windowGainedFocus

    public void windowLostFocus(WindowEvent e) {

        System.out.print("High:windowLostFocus ");
        System.out.println(((JFrame) e.getSource()).getTitle().substring(0, 1));
    }// windowLostFocus

    public void windowStateChanged(WindowEvent e) {

        System.out.println("High:windowStateChanged " + getState(e.getOldState()) + " to " + getState(e.getNewState()) + " "
                + ((JFrame) e.getSource()).getTitle().substring(0, 1));
    }// windowStateChanged

    // This method converts state values to text
    // descriptions. See constant values in Frame
    // class for the correlations between state
    // values and descriptive constants.
    String getState(int state) {

        if (state == Frame.NORMAL) {
            return "NORMAL";
        } else if (state == Frame.ICONIFIED) {
            return "ICONIFIED";
        } else if (state == Frame.MAXIMIZED_HORIZ) {
            return "MAXIMIZED_HORIZ";
        } else if (state == Frame.MAXIMIZED_VERT) {
            return "MAXIMIZED_VERT";
        } else if (state == Frame.MAXIMIZED_BOTH) {
            return "MAXIMIZED_BOTH";
        } else {
            return "Unknown state";
        }// end else
    }// end getState
}// end class WindowLstnr
// =============================================//

// An object of this class is instantiated to
// illustrate the use of focus on a Window that
// contains no focusable components. The
// coordinates of an invisible point are
// displayed on a Canvas in the JFrame near the
// location of the invisible point. When this
// object has the focus, pressing the arrow keys
// will change the coordinates of the invisible
// point, thus modifying and moving the displayed
// coordinate values.

class GraphicsGUI extends JFrame {

    // These are the coordinates of the invisible
    // point.
    int xCoor = 200;
    int yCoor = 40;

    public GraphicsGUI() {// constructor

        setBounds(0, 100, 400, 100);
        setTitle("B Copyright 2004 R.G.Baldwin");
        Display display = new Display();
        getContentPane().add(display);

        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // Prepare this object to handle low-level
        // WindowEvents using the process methods.
        enableEvents(AWTEvent.WINDOW_EVENT_MASK);

        // Prepare this object to handle high-level
        // KeyEvents using a listener.
        this.addKeyListener(new KeyListnr(display));

        // Prepare this object to handle high-level
        // focus and state events using a listener.
        addWindowFocusListener(new WindowLstnr());
        addWindowStateListener(new WindowLstnr());

        setVisible(true);
    }// end constructor

    // -------------------------------------------//

    // The next three methods are low-level event
    // handler methods that are new to V1.4. These
    // methods are identical to methods having the
    // same names defined for the class named
    // FocusWindow02, except that the screen output
    // identifies the object as B rather than A.

    // Sample screen output for this method:
    // Low:processWindowEvent B
    // 205 WINDOW_ACTIVATED
    protected void processWindowEvent(WindowEvent e) {

        System.out.println();
        System.out.println("Low:processWindowEvent B");
        System.out.print(e.getID() + " ");
        System.out.println(getEventType(e.getID()));
        super.processWindowEvent(e);
    }// end processWindowEvent

    // -------------------------------------------//

    // Sample screen output for this method:
    // Low:processWindowFocusEvent B
    // 207 WINDOW_GAINED_FOCUS
    protected void processWindowFocusEvent(WindowEvent e) {

        System.out.println();
        System.out.println("Low:processWindowFocusEvent B");
        System.out.print(e.getID() + " ");
        System.out.println(getEventType(e.getID()));
        super.processWindowFocusEvent(e);
    }// end processWindowFocusEvent

    // -------------------------------------------//

    // Sample screen output for this method:
    // Low:processWindowStateEvent B
    // 209 WINDOW_STATE_CHANGED
    protected void processWindowStateEvent(WindowEvent e) {

        System.out.println();
        System.out.println("Low:processWindowStateEvent B");
        System.out.print(e.getID() + " ");
        System.out.println(getEventType(e.getID()));
        super.processWindowStateEvent(e);
    }// end processWindowStateEvent

    // -------------------------------------------//

    // Method to convert event ID values to text
    // descriptions.
    String getEventType(int ID) {

        if (ID == WindowEvent.WINDOW_ACTIVATED) {
            return "WINDOW_ACTIVATED";
        } else if (ID == WindowEvent.WINDOW_CLOSED) {
            return "WINDOW_CLOSED";
        } else if (ID == WindowEvent.WINDOW_CLOSING) {
            return "WINDOW_CLOSING";
        } else if (ID == WindowEvent.WINDOW_DEACTIVATED) {
            return "WINDOW_DEACTIVATED";
        } else if (ID == WindowEvent.WINDOW_DEICONIFIED) {
            return "WINDOW_DEICONIFIED";
        } else if (ID == WindowEvent.WINDOW_ICONIFIED) {
            return "WINDOW_ICONIFIED";
        } else if (ID == WindowEvent.WINDOW_OPENED) {
            return "WINDOW_OPENED";
        } else if (ID == WindowEvent.WINDOW_STATE_CHANGED) {
            return "WINDOW_STATE_CHANGED";
        } else if (ID == WindowEvent.WINDOW_GAINED_FOCUS) {
            return "WINDOW_GAINED_FOCUS";
        } else if (ID == WindowEvent.WINDOW_LOST_FOCUS) {
            return "WINDOW_LOST_FOCUS";
        } else {
            return "Unknown event type";
        }// end else
    }// end getEventType
    // =============================================//

    // Begin inner class definitions

    class Display extends Canvas {

        Display() {// constructor

            // Set the focusable property to false to
            // guarantee that the JFrame contains no
            // focusable components.
            setFocusable(false);
        }// end constructor

        // Override the paint method to display the
        // coordinates on the screen near the location
        // specified by the coordinates.
        public void paint(Graphics g) {

            super.paint(g);
            g.drawString("" + xCoor + ", " + yCoor, xCoor, yCoor);
        }// end paint()
    }// end class Display
    // =============================================//

    // This listener class monitors for key events,
    // increments and displays the stored coordinates
    // when the arrow keys are pressed.
    class KeyListnr extends KeyAdapter {

        Display display;

        KeyListnr(Display display) {// constructor

            this.display = display;// save ref to display
        }// end constructor

        // -------------------------------------------//

        // Override the keyPressed method to increment
        // or decrement the coordinates by five pixels
        // when an arrow key is pressed. Then repaint
        // the canvas to cause the coordinate values to
        // be displayed. Remember y-coordinates are
        // displayed upside down.
        public void keyPressed(KeyEvent e) {

            int code = e.getKeyCode();

            if (code == e.VK_UP) {
                yCoor -= 5;
            } else if (code == e.VK_LEFT) {
                xCoor -= 5;
            } else if (code == e.VK_RIGHT) {
                xCoor += 5;
            } else if (code == e.VK_DOWN) {
                yCoor += 5;
            }// end else if

            display.repaint();// Display coordinates
        }// end keyPressed()
    }// end class KeyListnr
    // =============================================//
}// end GraphicsGUI class
// =============================================//

